home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Tool Chest / Development Tools & Languages / Dylan Related / Mindy / Mindy 1.2 - portable sources / libraries / print / print.dylan < prev   
Encoding:
Text File  |  1995-03-15  |  35.1 KB  |  1,129 lines  |  [TEXT/ttxt]

  1. module: Print
  2. author: chiles@cs.cmu.edu
  3. synopsis: This file implements object printing.
  4. copyright: See below.
  5. rcs-header: $Header: print.dylan,v 1.10 94/11/28 15:50:07 wlott Exp $
  6.  
  7. //======================================================================
  8. //
  9. // Copyright (c) 1994  Carnegie Mellon University
  10. // All rights reserved.
  11. // 
  12. // Use and copying of this software and preparation of derivative
  13. // works based on this software are permitted, including commercial
  14. // use, provided that the following conditions are observed:
  15. // 
  16. // 1. This copyright notice must be retained in full on any copies
  17. //    and on appropriate parts of any derivative works.
  18. // 2. Documentation (paper or online) accompanying any system that
  19. //    incorporates this software, or any part of it, must acknowledge
  20. //    the contribution of the Gwydion Project at Carnegie Mellon
  21. //    University.
  22. // 
  23. // This software is made available "as is".  Neither the authors nor
  24. // Carnegie Mellon University make any warranty about the software,
  25. // its performance, or its conformity to any specification.
  26. // 
  27. // Bug reports, questions, comments, and suggestions should be sent by
  28. // E-mail to the Internet address "gwydion-bugs@cs.cmu.edu".
  29. //
  30. //======================================================================
  31. //
  32.  
  33.  
  34.  
  35. /// <print-stream> class.
  36. ///
  37.  
  38. /// <print-stream> Class -- Exported.
  39. ///
  40. /// These streams hold print state so that the print function can do most
  41. /// of the work maintaining print state, and the print-object function can
  42. /// just print objects, querying the state of the stream as necessary.  Each
  43. /// slot defaults to the value of a global variable upon creation (see the
  44. /// comments for the print function).
  45. ///
  46. define sealed class <print-stream> (<stream>)
  47.   //
  48.   // Print-target holds the real destination of the print-stream.
  49.   slot print-target :: <stream>, required-init-keyword: #"stream";
  50.   //
  51.   // Print-level holds the maximum depth to which the user wants recursive
  52.   // printing to go.
  53.   slot print-level :: false-or(<fixed-integer>),
  54.     init-function: method () *default-level* end,
  55.     init-keyword: #"level";
  56.   //
  57.   // Print-depth holds the current level of printing.  When incremeting this
  58.   // slot causes the depth to exceed print-level, then the print function
  59.   // only outputs $print-level-exceeded-string.
  60.   slot print-depth :: <fixed-integer>, init-value: -1;
  61.   //
  62.   // Print-length holds the maximum number of elements the user wants a
  63.   // sequence to be printed.  This does not apply to some sequences, such as
  64.   // strings.
  65.   slot print-length :: false-or(<fixed-integer>),
  66.     init-function: method () *default-length* end,
  67.     init-keyword: #"length";
  68.   //
  69.   // Print-pretty? holds whether the user wants pretty printing.
  70.   slot print-pretty? :: <boolean>,
  71.     init-function: method () *default-pretty?* end,
  72.     init-keyword: #"pretty?";
  73.   //
  74.   // Print-circle? holds whether the user wants pretty printing.
  75.   slot print-circle? :: <boolean>,
  76.     init-function: method () *default-circle?* end,
  77.     init-keyword: #"circle?";
  78.   //
  79.   // Circular-first-pass? indicates to the print function whether it is on
  80.   // the first pass of printing, in which it just builds a table of objects
  81.   // referenced during the printing.  On the second pass of printing, print
  82.   // actually generates output.
  83.   slot circular-first-pass? :: <boolean>, init-value: #t;
  84.   //
  85.   // Circular-references is a table of objects referenced during printing
  86.   // when print-circle? is #t.
  87.   slot circular-references :: false-or(<object-table>),
  88.     init-value: #f;
  89.   //
  90.   // Circular-next-id holds the next ID to use when printing circularly.
  91.   // Each time print sees an object for a second time during the first
  92.   // printing pass, print assigns as the object's ID the current value of
  93.   // this slot.
  94.   slot circular-next-id :: <fixed-integer>, init-value: 0;
  95. end class;
  96.  
  97.  
  98.  
  99. /// <print-reference> Class.
  100. ///
  101.  
  102. /// <print-reference> Class -- Internal.
  103. ///
  104. /// These objects hold information about object references encountered when
  105. /// print-circle? is #t.  The print function creates these objects in a fake
  106. /// first printing pass, and then it uses these objects during a real second
  107. /// printing pass to determine whether the object needs to be tagged,
  108. /// printed normally, or printed by reference to the objects circular ID to
  109. /// avoid infinite recursive printing.
  110. ///
  111. define sealed class <print-reference> (<object>)
  112.   //
  113.   // This slot holds the object referenced during printing.
  114.   slot print-reference-object, init-keyword: #"object";
  115.   //
  116.   // This slot holds the object's ID for circular references.  The object
  117.   // prints as its ID after the first time.  Before the first time the object
  118.   // is printed, this slot is #f.
  119.   slot print-reference-id :: false-or(<byte-string>),
  120.     init-value: #f;
  121.   //
  122.   // This slot counts the number of references to the object.
  123.   slot print-reference-count :: <fixed-integer>, init-value: 0;
  124. end class;
  125.  
  126.  
  127.  
  128. /// Print-reference routines.
  129. ///
  130.  
  131. /// print-reference -- Internal Interface.
  132. ///
  133. /// This function returns the print-reference object associated with object.
  134. /// If none exists, then this creates a print-reference and installs it in
  135. /// the circular-references table.
  136. ///
  137. define method print-reference (object, stream :: <print-stream>)
  138.     => ref :: <print-reference>;
  139.   let table = stream.circular-references;
  140.   let ref = element(table, object, default: #f);
  141.   if (ref)
  142.     ref;
  143.   else
  144.     let ref = make(<print-reference>, object: object);
  145.     element(table, object) := ref;
  146.   end;
  147. end method;
  148.  
  149. /// new-print-reference-id -- Internal Interface.
  150. ///
  151. /// This function gets the next circular print reference ID, assigns it to ref,
  152. /// and updates the stream so that it doesn't return the same ID again.
  153. ///
  154. define method new-print-reference-id (stream :: <print-stream>,
  155.                       ref :: <print-reference>)
  156.     => ID :: <byte-string>;
  157.   let id = stream.circular-next-id;
  158.   stream.circular-next-id := id + 1;
  159.   ref.print-reference-id := integer-to-string(id);
  160. end method;
  161.  
  162. /// This vector is used by integer-to-string to convert digits to characters.
  163. ///
  164. define constant $digit-characters = "0123456789";
  165.  
  166. /// integer-to-string -- Internal.
  167. ///
  168. /// This converts a integer to a byte-string.
  169. ///
  170. /// This function makes the trade off that consing and throwing away a list
  171. /// (that probably never ascends to an elder GC generation) is better than
  172. /// isolating access to a global vector that lies around across calls to
  173. /// this function.  There was no profiling to validate this trade-off.
  174. ///
  175. define sealed method integer-to-string (arg :: <integer>)
  176.     => res :: <byte-string>;
  177.   local method repeat (arg, digits)
  178.       let (quotient, remainder) = floor/(arg, 10);
  179.       let digits = pair($digit-characters[remainder], digits);
  180.       if (zero?(quotient))
  181.         digits;
  182.       else
  183.         repeat(quotient, digits);
  184.       end;
  185.     end;
  186.   as(<byte-string>,
  187.      if (negative?(arg))
  188.        pair('-', repeat(- arg, #()));
  189.      else
  190.        repeat(arg, #());
  191.      end);
  192. end;
  193.  
  194.  
  195. /// Print-{level,length,depth,pretty?,circle?} generics and default methods.
  196. ///
  197.  
  198. /// print-length -- Exported.
  199. ///
  200. define sealed generic print-length (stream :: <stream>)
  201.     => length :: false-or(<fixed-integer>);
  202.  
  203. define method print-length (stream :: <stream>)
  204.     => length :: singleton(#f);
  205.   #f;
  206. end method;
  207.  
  208.  
  209. /// print-level -- Exported.
  210. ///
  211. define sealed generic print-level (stream :: <stream>)
  212.     => level :: false-or(<fixed-integer>);
  213.  
  214. define method print-level (stream :: <stream>)
  215.     => level :: singleton(#f);
  216.   #f;
  217. end method;
  218.  
  219.  
  220. /// print-depth -- Exported.
  221. ///
  222. define sealed generic print-depth (stream :: <stream>)
  223.     => depth :: <fixed-integer>;
  224.  
  225. define method print-depth (stream :: <stream>)
  226.     => depth :: singleton(0);
  227.   0;
  228. end method;
  229.  
  230.  
  231. /// print-pretty? -- Exported.
  232. ///
  233. define sealed generic print-pretty? (stream :: <stream>)
  234.     => pretty? :: <boolean>;
  235.  
  236. define method print-pretty? (stream :: <stream>)
  237.     => pretty? :: singleton(#f);
  238.   #f;
  239. end method;
  240.  
  241.  
  242. /// print-circle? -- Exported.
  243. ///
  244. define sealed generic print-circle? (stream :: <stream>)
  245.     => circle? :: <boolean>;
  246.  
  247. define method print-circle? (stream :: <stream>)
  248.     => circle? :: singleton(#f);
  249.   #f;
  250. end method;
  251.  
  252.  
  253.  
  254. /// Print and global defaults.
  255. ///
  256.  
  257. /// These provide the default values for the keywords to print.  #f means
  258. /// there are no bounds, special checks for circularity, or pretty printing.
  259. ///
  260. define variable *default-level* :: false-or(<fixed-integer>) = #f;
  261. define variable *default-length* :: false-or(<fixed-integer>) = #f;
  262. define variable *default-circle?* :: <boolean> = #f;
  263. define variable *default-pretty?* :: <boolean> = #f;
  264.  
  265. /// Get a unique address to use as the default value for the print function's
  266. /// keyword arguments so that it can tell when the user supplies keywords.
  267. ///
  268. define constant $unsupplied-arg = pair(#f, #f);
  269.  
  270. /// What to print when the current depth exceeds the users requested print
  271. /// level limit.
  272. ///
  273. define constant $print-level-exceeded-string :: <byte-string> = "#";
  274.  
  275. /// What to print before a circular print ID.
  276. ///
  277. define constant $circular-id-prestring :: <byte-string> = "#";
  278.  
  279. /// What to print after a circular print ID.
  280. ///
  281. define constant $circular-id-poststring :: <byte-string> = "#";
  282.  
  283.  
  284. /// Print -- Exported.
  285. ///
  286. define generic print (object, stream :: <stream>,
  287.               #key level, length, circle?, pretty?)
  288.     => ();
  289.  
  290.  
  291. /// Print -- Method for Exported Interface.
  292. ///
  293. /// This method must regard the values of the keywords and construct a
  294. /// <print-stream> to hold the values for the requested print operation.
  295. ///
  296. define method print (object, stream :: <stream>,
  297.              #key level = $unsupplied-arg,
  298.                   length = $unsupplied-arg,
  299.                   circle? = $unsupplied-arg,
  300.                   pretty? = $unsupplied-arg)
  301.     => ();
  302.   block ()
  303.     //
  304.     // Lock the stream so that all the calls to print-object build output
  305.     // contiguously, without intervening threads screwing up the print
  306.     // request.
  307.     lock-stream(stream);
  308.     //
  309.     // Make the stream defaulting the slots to the global default values for
  310.     // the keyword arguments.  No need to lock this stream because only this
  311.     // thread should have any references to it ... barring extreme user
  312.     // silliness.
  313.     let p-stream = make(<print-stream>, stream: stream);
  314.     //
  315.     // Set slots with those values supplied by the user.
  316.     if (~ (level == $unsupplied-arg)) p-stream.print-level := level end;
  317.     if (~ (length == $unsupplied-arg)) p-stream.print-length := length end;
  318.     if (~ (circle? == $unsupplied-arg)) p-stream.print-circle? := circle? end;
  319.     if (~ (pretty? == $unsupplied-arg)) p-stream.print-pretty? := pretty? end;
  320.     //
  321.     // When printing circularly, we first print to a "null stream" so that we
  322.     // can find the circular references.
  323.     if (p-stream.print-circle?)
  324.       start-circle-printing(object, p-stream);
  325.     end;
  326.     //
  327.     // Determine whether, and how, to print object.
  328.     maybe-print-object(object, p-stream);
  329.   cleanup
  330.     unlock-stream(stream);
  331.   end;
  332. end method;
  333.  
  334. /// Print -- Method for Exported Interface.
  335. ///
  336. /// This method must regard the values of the keywords and construct a
  337. /// <print-stream> to hold the values for the requested print operation.
  338. ///
  339. define method print (object, stream :: <print-stream>,
  340.              #key level = $unsupplied-arg,
  341.               length = $unsupplied-arg,
  342.                   circle? = $unsupplied-arg,
  343.                   pretty? = $unsupplied-arg)
  344.     => ();
  345.   let save-level = stream.print-level;
  346.   let save-length = stream.print-length;
  347.   let save-circle? = stream.print-circle?;
  348.   let save-pretty? = stream.print-pretty?;
  349.   block ()
  350.     //
  351.     // Establish changes in policy for this call to print.
  352.     // If level is supplied, and there was already a level in effect, we
  353.     // continue printing with the minimum effect of the two levels, assuming
  354.     // that is the most careful thing to do.
  355.     case
  356.       (level = $unsupplied-arg) => #f;   // Case is broken in Mindy.
  357.       (save-level) =>
  358.     stream.print-level := min(save-level, (level + stream.print-depth));
  359.       otherwise => stream.print-level := level;
  360.     end;
  361.     // If length is supplied, and there was already a length in effect, we
  362.     // continue printing with the minimum of the two lengths, assuming that
  363.     // is the most careful thing to do.
  364.     case
  365.       (length = $unsupplied-arg) => #f;   // Case is broken in Mindy.
  366.       (save-length) => stream.print-length := min(save-length, length);
  367.       otherwise => stream.print-length := length;
  368.     end;
  369.     // We never turn off circular printing, but if a recursive call to print
  370.     // turns circular printing on, we print that object circularly.
  371.     case
  372.       ((circle? = $unsupplied-arg) | (~ circle?)) =>
  373.     #f;   // Case is broken in Mindy.
  374.       (~ save-circle?) =>
  375.     stream.print-circle? := #t;
  376.     start-circle-printing(object, stream);
  377.     end;
  378.     // Printing pretty gets turned on and off for each user-supplied value
  379.     // passed to print.  The assumption is that there is no harm in turning
  380.     // it off for some object, and because it is odd to request no pretty
  381.     // printing, the calling code probably has good reason to turn it off.
  382.     if (~ (pretty? == $unsupplied-arg)) stream.print-pretty? := pretty? end;
  383.     //
  384.     // Determine whether, and how, to print object.
  385.     maybe-print-object(object, stream);
  386.   cleanup
  387.     stream.print-level := save-level;
  388.     stream.print-length := save-length;
  389.     stream.print-circle? := save-circle?;
  390.     stream.print-pretty? := save-pretty?;
  391.   end;
  392. end method;
  393.  
  394. /// start-circle-printing -- Internal.
  395. ///
  396. /// This function makes sure the stream has a circular-references table,
  397. /// makes sure object has a print-reference, checks for circular references
  398. /// within object, and considers what sort of output may be necessary to
  399. /// define a tag for object or print object's tag.
  400. ///
  401. /// This function is called both from the very first call to print and
  402. /// recursive calls to print.  The calls to start-circle-printing within
  403. /// recursive calls to print occur when the original call to print had
  404. /// circular printing turned off, and the recursive calls to print turn
  405. /// circular printing on.  Because of this function's use within recursive
  406. /// calls to print, it cannot make certain assumptions:
  407. ///    Whether stream already has a circular-references table.
  408. ///    Whether there already is a print-reference for object.
  409. ///    What print-reference-count is for object.
  410. ///    Whether to do a first pass on object looking for circular references.
  411. ///    Whether object already has a print-reference-id.
  412. ///
  413. /// Recursive calls to print cannot turn off circular printing, so we don't
  414. /// have to account for that.
  415. ///
  416. define method start-circle-printing (object, stream :: <print-stream>)
  417.     => ();
  418.   let table = stream.circular-references;
  419.   if (~ table)
  420.     table := make(<object-table>);
  421.     stream.circular-references := table;
  422.   end;
  423.   let ref = print-reference(object, stream);
  424.   let count :: <fixed-integer> = (ref.print-reference-count + 1);
  425.   ref.print-reference-count := count;
  426.   if (count = 1)
  427.     // If this is the first time we've seen this object, then dive into it
  428.     // looking for circular references.
  429.     stream.circular-first-pass? := #t;
  430.     print-object(object, stream);
  431.     stream.circular-first-pass? := #f;
  432.   end;
  433. end method;
  434.  
  435. /// maybe-print-object -- Internal.
  436. ///
  437. /// This function increments print-depth and regards print-level to see
  438. /// whether it should print object.  If it should print object, then it
  439. /// regards print-circle? and does the right thing.
  440. ///
  441. define method maybe-print-object (object, stream :: <print-stream>)
  442.   let depth :: <fixed-integer> = (stream.print-depth + 1);
  443.   block ()
  444.     stream.print-depth := depth;
  445.     let requested-level :: false-or(<fixed-integer>) = stream.print-level;
  446.     case
  447.       (requested-level & (depth > requested-level)) =>
  448.     write($print-level-exceeded-string, stream);
  449.       (~ stream.print-circle?) =>
  450.     print-object(object, stream);
  451.       (stream.circular-first-pass?) =>
  452.     // When printing circularly, we first print to a "null stream" so
  453.     // that we can find the circular references.
  454.     let ref = print-reference(object, stream);
  455.     let ref-count = (ref.print-reference-count + 1);
  456.     ref.print-reference-count := ref-count;
  457.     if (ref-count = 1)
  458.       // If ref-count is already greater than one, then there's
  459.       // no reason to go further into the object gathering references.
  460.       print-object(object, stream);
  461.     end;
  462.       otherwise
  463.     output-print-reference(print-reference(object, stream),
  464.                    stream);
  465.     end case;
  466.   cleanup
  467.     stream.print-depth := depth - 1;
  468.   end;
  469. end method;
  470.  
  471. /// output-print-reference -- Internal.
  472. ///
  473. /// This function determines how to output a print-reference for circular
  474. /// printing.
  475. ///
  476. define method output-print-reference (ref :: <print-reference>,
  477.                       stream :: <stream>)
  478.     => ();
  479.   let ref-id = ref.print-reference-id;
  480.   case
  481.     (ref.print-reference-count = 1) =>
  482.       print-object(ref.print-reference-object, stream);
  483.     (~ ref-id) =>
  484.       write($circular-id-prestring, stream);
  485.       write(new-print-reference-id(stream, ref), stream);
  486.       write($circular-id-poststring, stream);
  487.       write("=", stream);
  488.       print-object(ref.print-reference-object, stream);
  489.     otherwise =>
  490.       write($circular-id-prestring, stream);
  491.       write(ref-id, stream);
  492.       write($circular-id-poststring, stream);
  493.   end;
  494. end method;
  495.  
  496.  
  497.  
  498. /// Print-object generic and default method.
  499. ///
  500.  
  501. /// print-object -- Exported.
  502. ///
  503. define generic print-object (object, stream :: <stream>)
  504.     => ();
  505.  
  506. /// Any object.
  507. ///
  508. /// This method prints as many slot value pairs as it can without exceeding
  509. /// print-length and counting each pair as two elements.  This method does
  510. /// not count "Foo instance" in any way in the length calculation.
  511. ///
  512. define method print-object (object :: <object>, stream :: <stream>)
  513.     => ();
  514.   pprint-logical-block
  515.     (stream,
  516.      prefix: "{",
  517.      body: method (stream)
  518.          let obj-class = object.object-class;
  519.          write-class-name(obj-class, stream);
  520.          write(" instance", stream);
  521.          let descriptors = obj-class.slot-descriptors;
  522.          if (~ (descriptors = #()))
  523.            write(", ", stream);
  524.            pprint-indent(#"block", 2, stream);
  525.            pprint-newline(#"linear", stream);
  526.            // Print slot names and values.
  527.            pprint-logical-block
  528.          (stream,
  529.           prefix: #f,
  530.           body: method (stream)
  531.               block (exit)
  532.                 let length :: false-or(<fixed-integer>)
  533.                   = stream.print-length;
  534.                 for (desc in descriptors,
  535.                  // Count each slot name and value as two
  536.                  // for considerations of print-length.
  537.                  count = 0 then (count + 2))
  538.                   if (count ~= 0)
  539.                 write(", ", stream);
  540.                 pprint-newline(#"linear", stream);
  541.                   end;
  542.                   if (length & (count >= length))
  543.                 write("...", stream);
  544.                 exit();
  545.                   end;
  546.                   write(as(<byte-string>, desc.slot-name), stream);
  547.                   write(": ", stream);
  548.                   pprint-newline(#"fill", stream);
  549.                   let (value, win?) = slot-value(desc, object);
  550.                   if (win?)
  551.                 print(value, stream);
  552.                   else
  553.                 write("{UNINITIALIZED}", stream);
  554.                   end;
  555.                 end for;
  556.               end block;
  557.             end method,
  558.           suffix: #f);
  559.          end if;
  560.        end method,
  561.      suffix: "}");
  562. end method;
  563.  
  564.  
  565.  
  566. /// Print-object <byte-string> and <byte-character> methods.
  567. ///
  568.  
  569. /// This is used in the print-object method for <byte-string>.
  570. ///
  571. define constant byte-string-escape-chars
  572.     = make(<vector>, size: 256, fill: #f);
  573. byte-string-escape-chars[as(<byte>, '\0')] := '0';
  574. byte-string-escape-chars[as(<byte>, '\a')] := 'a';
  575. byte-string-escape-chars[as(<byte>, '\b')] := 'b';
  576. byte-string-escape-chars[as(<byte>, '\t')] := 't';
  577. byte-string-escape-chars[as(<byte>, '\f')] := 'f';
  578. byte-string-escape-chars[as(<byte>, '\r')] := 'r';
  579. byte-string-escape-chars[as(<byte>, '\n')] := 'n';
  580. byte-string-escape-chars[as(<byte>, '\e')] := 'e';
  581. byte-string-escape-chars[as(<byte>, '"')] := '"';
  582. byte-string-escape-chars[as(<byte>, '\\')] := '\\';
  583.  
  584. /// Byte-strings.
  585. ///
  586. define method print-object (object :: <byte-string>, stream :: <stream>)
  587.     => ();
  588.   write('"', stream);
  589.   let i :: <fixed-integer> = 0;
  590.   let len :: <fixed-integer> = object.size;
  591.   while (i < len)
  592.     // Find a char that requires an escape (call it the special char).
  593.     for (j :: <fixed-integer> = i then (j + 1),
  594.      until ((j = len) | byte-string-escape-chars[as(<byte>, object[j])]))
  595.     finally
  596.       // Print from the last special char to this one.
  597.       write(object, stream, start: i, end: j);
  598.       // Print the escape character followed by the special character.
  599.       if (j < len)
  600.     write('\\', stream);
  601.     write(byte-string-escape-chars[as(<byte>, object[j])], stream);
  602.       end;
  603.       // Move past the special character.
  604.       i := (j + 1);
  605.     end;
  606.   end;
  607.   write('"', stream);
  608. end method;
  609.  
  610. /// Byte-characters.
  611. ///
  612. define method print-object (object :: <byte-character>, stream :: <stream>)
  613.     => ();
  614.   write('\'', stream);
  615.   case
  616.     (byte-string-escape-chars[as(<byte>, object)]) =>
  617.     write('\\', stream);
  618.     write(byte-string-escape-chars[as(<byte>, object)], stream);
  619.     (object = '\'') =>
  620.     write('\\', stream);
  621.     write('\'', stream);
  622.     otherwise =>
  623.       write(object, stream);
  624.   end;
  625.   write('\'', stream);
  626. end method;
  627.  
  628.  
  629.  
  630. /// Print-object <list> method.
  631. ///
  632.  
  633. /// For circular printing to be correct, we need to count references to the
  634. /// tail pointers as well as the head pointers.  Because we do not print lists
  635. /// by calling print on the tail of each pair, we need to specially handle
  636. /// the tail pointers in this method.  The object passed in and all head
  637. /// pointers are handled naturally via calls to print.
  638. ///
  639. define method print-object (object :: <list>, stream :: <stream>) => ();
  640.   pprint-logical-block(stream,
  641.                prefix: "#(",
  642.                body: method (stream)
  643.                    if (~ (object == #()))
  644.                  print-list(object, stream);
  645.                    end;
  646.                  end,
  647.                suffix: ")");
  648. end method;
  649.  
  650. define method print-list (object :: <list>, stream :: <stream>) => ();
  651.   block(exit)
  652.     let length :: false-or(<fixed-integer>) = stream.print-length;
  653.     if (length & (length <= 0))
  654.       write("...", stream);
  655.     else
  656.       print(object.head, stream);
  657.       let circle? = stream.print-circle?;
  658.       let first-pass? = stream.circular-first-pass?;
  659.       for (remaining = object.tail then remaining.tail,
  660.        count = 1 then (count + 1),
  661.        until (remaining == #()))
  662.     write(", ", stream);
  663.     pprint-newline(#"fill", stream);
  664.     case
  665.       (~ instance?(remaining, <list>)) =>
  666.         // Object was not a proper list, so print dot notation.
  667.         write(". ", stream);
  668.         pprint-newline(#"fill", stream);
  669.         print(remaining, stream);
  670.         exit();
  671.       (length & (count >= length)) =>
  672.         // We've exceeded print-length for this print request.
  673.         write("...", stream);
  674.         exit();
  675.       (~ circle?) =>
  676.         // No circular printing, so this is the simple and normal case.
  677.         print(remaining.head, stream);
  678.       (first-pass?) =>
  679.         // Get or create the print-reference for the remaining pointer.
  680.         let ref = print-reference(remaining, stream);
  681.         let ref-count = (ref.print-reference-count + 1);
  682.         ref.print-reference-count := ref-count;
  683.         if (ref-count = 1)
  684.           // First time through, so keep gathering references.
  685.           print(remaining.head, stream);
  686.         else
  687.           // If ref-count is already greater than one, then we've seen
  688.           // everything once.  Stop iterating.
  689.           exit();
  690.         end;
  691.       otherwise =>
  692.         // Circular printing on the second pass.
  693.         let ref = print-reference(remaining, stream);
  694.         let ref-id = ref.print-reference-id;
  695.         case
  696.           (ref.print-reference-count = 1) =>
  697.         // Only one reference to the rest of the list, so print the
  698.         // remaining elements normally.
  699.         print(remaining.head, stream);
  700.           (~ ref-id) =>
  701.         // Print the tag and its value with dot notation so that
  702.         // the rest of the list does not appear to be a single
  703.         // element of the list (that is, a nested list).
  704.         write(". ", stream);
  705.         pprint-newline(#"fill", stream);
  706.         write($circular-id-prestring, stream);
  707.         write(new-print-reference-id(stream, ref), stream);
  708.         write($circular-id-poststring, stream);
  709.         write("=", stream);
  710.         print(remaining, stream);
  711.           otherwise =>
  712.         // Print the tag with dot notation.  See previous cases's
  713.         // comment.
  714.         write(". ", stream);
  715.         pprint-newline(#"fill", stream);
  716.         write($circular-id-prestring, stream);
  717.         write(ref-id, stream);
  718.         write($circular-id-poststring, stream);
  719.         exit();
  720.         end case;
  721.     end case;
  722.       end for;
  723.     end if;
  724.   end block;
  725. end method;
  726.  
  727.  
  728. /// Print-object <simple-object-vector> method.
  729. ///
  730.  
  731. /// Vectors.
  732. ///
  733. define method print-object (object :: <simple-object-vector>,
  734.                 stream :: <stream>)
  735.     => ();
  736.   pprint-logical-block(stream,
  737.                prefix: "#[",
  738.                body: method (stream)
  739.                    print-items(object, print, stream);
  740.                  end method,
  741.                suffix: "]");
  742. end method;
  743.  
  744.  
  745.  
  746. /// Print-object <function> method.
  747. ///
  748.  
  749. /// Functions.
  750. ///
  751. define method print-object (object :: <function>, stream :: <stream>)
  752.     => ();
  753.   pprint-logical-block
  754.     (stream,
  755.      prefix: "{",
  756.      body: method (stream)
  757.          case
  758.            (instance?(object, <generic-function>)) =>
  759.          write("GF", stream);
  760.          let name = function-name(object);
  761.          if (name)
  762.            write(' ', stream);
  763.            pprint-newline(#"fill", stream);
  764.            write(as(<byte-string>, name), stream);
  765.          end;
  766.            (instance?(object, <method>)) =>
  767.          write("Method", stream);
  768.          let name = function-name(object);
  769.          if (name)
  770.            write(' ', stream);
  771.            pprint-newline(#"fill", stream);
  772.            write(as(<byte-string>, name), stream);
  773.          end;
  774.          print-function-specializers(object, stream);
  775.            otherwise =>
  776.          write("Function", stream);
  777.          end
  778.        end,
  779.      suffix: "}");
  780. end method;
  781.  
  782. define method print-function-specializers (object :: <function>,
  783.                        stream :: <stream>)
  784.     => ();
  785.   let specializers = method-specializers(object);
  786.   if (~ (specializers = #()))
  787.     write(' ', stream);
  788.     pprint-newline(#"fill", stream);
  789.     pprint-logical-block
  790.       (stream,
  791.        prefix: "(",
  792.        body: method (stream)
  793.            print-items(specializers, print-specializer, stream);
  794.          end,
  795.        suffix: ")");
  796.   end if;
  797. end method;
  798.  
  799. /// print-items -- Internal Interface.
  800. ///
  801. /// This function prints each element of items, separated by commas, using
  802. /// print-fun.  This function also regards print-length.  Stream must be a
  803. /// pretty printing stream or a <print-stream> whose target is a pretty
  804. /// printing stream, so this function is basically good for use in body:
  805. /// methods passed to pprint-logical-block.
  806. ///
  807. /// Do not use this function for collections that may be tail-circular; it
  808. /// will not terminate.
  809. ///
  810. define method print-items (items :: <collection>, print-fun :: <function>,
  811.                stream :: <stream>)
  812.     => ();
  813.   block (exit)
  814.     let length :: false-or(<fixed-integer>)
  815.       = stream.print-length;
  816.     let stream-for-apply = list(stream);
  817.     for (x in items,
  818.      count = 0 then (count + 1))
  819.       if (count ~= 0)
  820.     write(", ", stream);
  821.     pprint-newline(#"fill", stream);
  822.       end;
  823.       if (length & (count = length))
  824.     write("...", stream);
  825.     exit();
  826.       end;
  827.       apply(print-fun, x, stream-for-apply);
  828.     end for;
  829.   end block;
  830. end method;
  831.  
  832.  
  833.  
  834. /// Print-specializer generic function and methods.
  835. ///
  836.  
  837. /// This function is used in printing methods.
  838. ///
  839.  
  840. define sealed generic print-specializer (type :: <type>, stream :: <stream>)
  841.     => ();
  842.  
  843. define method print-specializer (type :: <type>, stream :: <stream>) => ();
  844.   write("{UNKNOWN-TYPE}", stream);
  845. end method;
  846.  
  847. define method print-specializer (type :: <class>, stream :: <stream>)
  848.     => ();
  849.   write-class-name(type, stream);
  850. end method;
  851.  
  852. define method print-specializer (type :: <singleton>, stream :: <stream>)
  853.     => ();
  854.   write("{Singleton ", stream);
  855.   print(type.singleton-object, stream);
  856.   write("}", stream);
  857. end method;
  858.  
  859. define method print-specializer (type :: <subclass>, stream :: <stream>)
  860.     => ();
  861.   write("{Subclasses of ", stream);
  862.   write-class-name(type.subclass-of, stream);
  863.   write("}", stream);
  864. end method;
  865.  
  866. define method print-specializer (type :: <limited-integer>, stream :: <stream>)
  867.     => ();
  868.   write("{Limited ", stream);
  869.   write-class-name(type.limited-integer-class, stream);
  870.   write(' ', stream);
  871.   print(type.limited-integer-min, stream);
  872.   write("..", stream);
  873.   print(type.limited-integer-max, stream);
  874.   write("}", stream);
  875. end method;
  876.  
  877. define method print-specializer (type :: <union>, stream :: <stream>)
  878.     => ();
  879.   pprint-logical-block
  880.     (stream,
  881.      prefix: "{",
  882.      body: method (stream)
  883.          write("Union ", stream);
  884.          pprint-newline(#"fill", stream);
  885.          print(type.union-members, stream);
  886.        end method,
  887.      suffix: "}");
  888. end method;
  889.  
  890.  
  891.  
  892. /// Print-object <class> method.
  893. ///
  894.  
  895. /// Classes.
  896. ///
  897. define method print-object (object :: <class>, stream :: <stream>) => ();
  898.   write("{Class ", stream);
  899.   write-class-name(object, stream);
  900.   write("}", stream);
  901. end method;
  902.  
  903. /// write-class-name -- Internal Interface.
  904. ///
  905. /// This function writes the name of the class or "<UNNAMED-CLASS>" to stream.
  906. /// It does not output any curly braces, the word "class", or anything else.
  907. ///
  908. define method write-class-name (object :: <class>, stream :: <stream>)
  909.     => ();
  910.   let name = class-name(object);
  911.   if (name)
  912.     write(as(<byte-string>, name), stream);
  913.   else
  914.     write("<UNNAMED-CLASS>", stream);
  915.   end;
  916. end method;
  917.  
  918.  
  919.  
  920. /// Print-object miscellaneous methods.
  921. ///
  922.  
  923. /// #t.
  924. ///
  925. define method print-object (object :: singleton(#t), stream :: <stream>)
  926.     => ();
  927.   write("#t", stream);
  928. end method;
  929.  
  930. /// #f.
  931. ///
  932. define method print-object (object :: singleton(#f), stream :: <stream>)
  933.     => ();
  934.   write("#f", stream);
  935. end method;
  936.  
  937. /// Symbols.
  938. ///
  939. define method print-object (object :: <symbol>, stream :: <stream>) => ();
  940.   write("#\"", stream);
  941.   write(as(<string>, object), stream);
  942.   write('"', stream);
  943. end method;
  944.  
  945. /// Integers.
  946. ///
  947. define method print-object (object :: <fixed-integer>, stream :: <stream>)
  948.     => ();
  949.   write(integer-to-string(object), stream);
  950. end method;
  951. ///
  952. define method print-object (object :: <extended-integer>, stream :: <stream>)
  953.     => ();
  954.   write("#e", stream);
  955.   write(integer-to-string(object), stream);
  956. end method;
  957.  
  958.  
  959.  
  960.  
  961. /// print-to-string -- Exported.
  962. ///
  963. define generic print-to-string (object, #rest args,
  964.                 #key level, length, circle?, pretty?)
  965.     => result :: <string>;
  966.  
  967. define method print-to-string (object, #rest args,
  968.                    #key level, length, circle?, pretty?)
  969.     => result :: <byte-string>;
  970.   let s = make(<byte-string-output-stream>);
  971.   apply(print, object, s, args);
  972.   s.string-output-stream-string;
  973. end method;
  974.  
  975.  
  976.  
  977. /// Streams protocol extensions for <print-stream>s.
  978. ///
  979.  
  980. /// These methods may change when pretty printing goes in.  In particular,
  981. /// getting and releasing the buffer may interact with buffered pretty
  982. /// printing stuff.
  983. ///
  984.  
  985. define constant bogus-buffer = make(<buffer>);
  986.  
  987. define method stream-extension-get-output-buffer (stream :: <print-stream>)
  988.     => (buffer :: <buffer>, next :: <buffer-index>, size :: <buffer-index>);
  989.   if ((stream.print-circle?) & (stream.circular-first-pass?))
  990.     values(bogus-buffer, 0, bogus-buffer.size);
  991.   else
  992.     stream-extension-get-output-buffer(stream.print-target);
  993.   end;
  994. end method;
  995.  
  996. define method stream-extension-release-output-buffer
  997.     (stream :: <print-stream>, next :: <buffer-index>)
  998.     => ();
  999.   if (~ ((stream.print-circle?) & (stream.circular-first-pass?)))
  1000.     stream-extension-release-output-buffer(stream.print-target, next);
  1001.   end;
  1002. end method;
  1003.  
  1004. define method stream-extension-empty-output-buffer
  1005.     (stream :: <print-stream>, stop :: <buffer-index>)
  1006.     => ();
  1007.   if (~ ((stream.print-circle?) & (stream.circular-first-pass?)))
  1008.     stream-extension-empty-output-buffer(stream.print-target, stop);
  1009.   end;
  1010. end method;
  1011.  
  1012. define method stream-extension-force-secondary-buffers
  1013.     (stream :: <print-stream>)
  1014.     => ();
  1015.   if (~ ((stream.print-circle?) & (stream.circular-first-pass?)))
  1016.     stream-extension-force-secondary-buffers(stream.print-target);
  1017.   end;
  1018. end method;
  1019.  
  1020. define method stream-extension-synchronize (stream :: <print-stream>)
  1021.     => ();
  1022.   if (~ ((stream.print-circle?) & (stream.circular-first-pass?)))
  1023.     stream-extension-synchronize(stream.print-target);
  1024.   end;
  1025. end method;
  1026.  
  1027.  
  1028.  
  1029. /// Pretty-printer support.
  1030.  
  1031. /// The methods on this page extend the pprint interface to <print-stream>s.
  1032. /// Doing this allows users to write print-object methods that attempt to do
  1033. /// pretty printing, but when print is called with pretty?: #f, all the
  1034. /// pretty printing directions in the print-object method become no-ops.
  1035. ///
  1036.  
  1037. /// pprint-logical-block -- Method for Exported Interface.
  1038. ///
  1039. /// When pretty printing, we pass the print-target of the <print-stream> to
  1040. /// the recursive call to pprint-logical-block.  This causes
  1041. /// pprint-logical-block to wrap a pretty printing stream around the actual
  1042. /// target.  The body: method of the recursive call then wraps the
  1043. /// <print-stream> around the pretty printing stream, nesting the ultimate
  1044. /// target stream twice.
  1045. ///
  1046. /// In the body: method of the recursive call, there is a check to see if
  1047. /// the target is the pretty-stream argument. They are == when the body
  1048. /// function passed to this <print-stream> method contains recursive calls
  1049. /// to pprint-logical-block.  The code works without the if test, but
  1050. /// besides saving a few stores into memory with the assignments, the code
  1051. /// seemed more clear with the if test; that is, it should be more clear to
  1052. /// future maintainers of this code that the method can be reentered on the
  1053. /// same stream and what happens when this method is reentered.
  1054. ///
  1055. define method pprint-logical-block (stream :: <print-stream>,
  1056.                     #key column = 0, prefix, per-line-prefix,
  1057.                      body, suffix)
  1058.     => ();
  1059.   if (prefix & per-line-prefix)
  1060.     error("Can't specify both a prefix: and a per-line-prefix:");
  1061.   end;
  1062.   case
  1063.     ((stream.print-circle?) & (stream.circular-first-pass?)) =>
  1064.       #f;   // Case is broken in Mindy.
  1065.     (stream.print-pretty?) =>
  1066.       let target = stream.print-target;
  1067.       pprint-logical-block(target,
  1068.                column: column,
  1069.                prefix: prefix,
  1070.                per-line-prefix: per-line-prefix,
  1071.                body: method (pretty-stream)
  1072.                    if (pretty-stream == target)
  1073.                      body(stream);
  1074.                    else
  1075.                      let orig-target = stream.print-target;
  1076.                      stream.print-target := pretty-stream;
  1077.                      body(stream);
  1078.                      stream.print-target := orig-target;
  1079.                    end;
  1080.                  end,
  1081.                suffix: suffix);
  1082.     otherwise =>
  1083.       if (prefix | per-line-prefix)
  1084.     write(prefix | per-line-prefix, stream);
  1085.       end;
  1086.       body(stream);
  1087.       if (suffix)
  1088.     write(suffix, stream);
  1089.       end;
  1090.   end case;
  1091. end method;
  1092.  
  1093. /// pprint-newline -- Method for Exported Interface.
  1094. ///
  1095. define method pprint-newline (kind :: one-of(#"linear", #"miser", #"fill",
  1096.                          #"mandatory"),
  1097.                   stream :: <print-stream>)
  1098.     => ();
  1099.   case
  1100.     ((~ ((stream.print-circle?) & (stream.circular-first-pass?)))
  1101.        & stream.print-pretty?) =>
  1102.       pprint-newline(kind, stream.print-target);
  1103.     (kind == #"mandatory") =>
  1104.       write('\n', stream);
  1105.   end;
  1106. end;
  1107.  
  1108. define method pprint-indent (relative-to :: one-of(#"block", #"current"),
  1109.                  n :: <fixed-integer>,
  1110.                  stream :: <print-stream>)
  1111.     => ();
  1112.   if ((~ ((stream.print-circle?) & (stream.circular-first-pass?)))
  1113.     & stream.print-pretty?)
  1114.     pprint-indent(relative-to, n, stream.print-target);
  1115.   end;
  1116. end;
  1117.  
  1118. define method pprint-tab (kind :: one-of(#"line", #"section", #"line-relative",
  1119.                      #"section-relative"),
  1120.               colnum :: <fixed-integer>,
  1121.               colinc :: <fixed-integer>,
  1122.               stream :: <print-stream>)
  1123.     => ();
  1124.   if ((~ ((stream.print-circle?) & (stream.circular-first-pass?)))
  1125.     & stream.print-pretty?)
  1126.     pprint-tab(kind, colnum, colinc, stream.print-target);
  1127.   end;
  1128. end;
  1129.